#pragma once

//##########################################################################
//#                                                                        #
//#                     CLOUDCOMPARE PLUGIN: qFacets                       #
//#                                                                        #
//#  This program is free software; you can redistribute it and/or modify  #
//#  it under the terms of the GNU General Public License as published by  #
//#  the Free Software Foundation; version 2 or later of the License.      #
//#                                                                        #
//#  This program is distributed in the hope that it will be useful,       #
//#  but WITHOUT ANY WARRANTY; without even the implied warranty of        #
//#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          #
//#  GNU General Public License for more details.                          #
//#                                                                        #
//#                      COPYRIGHT: Thomas Dewez, BRGM                     #
//#                                                                        #
//##########################################################################

//CCCoreLib
#include <FastMarching.h>
#include <GenericProgressCallback.h>
#include <DistanceComputationTools.h>

//qCC_db
#include <ccAdvancedTypes.h>

class ccGenericPointCloud;
class ccPointCloud;

//! Fast Marching algorithm for planar facets extraction (qFacets plugin)
/** Extends the FastMarching class.
**/
class FastMarchingForFacetExtraction : public CCCoreLib::FastMarching
{
public:

	//! Static entry point (helper)
	static int ExtractPlanarFacets(	ccPointCloud* theCloud,
									unsigned char octreeLevel,
									ScalarType maxError,
									CCCoreLib::DistanceComputationTools::ERROR_MEASURES errorMeasure,
									bool useRetroProjectionError = true,
									CCCoreLib::GenericProgressCallback* progressCb = nullptr,
									CCCoreLib::DgmOctree* _theOctree = nullptr);

	//! Default constructor
	FastMarchingForFacetExtraction();

	//! Destructor
	~FastMarchingForFacetExtraction() override;

	//! Initializes the grid with a point cloud (and ist corresponding octree)
	/** The points should be associated to an (active) scalar field.
		The Fast Marching grid will have the same dimensions as
		the input octree considered at a given level of subdivision.
		\param theOctree an octree, associated to a point cloud
		\param gridLevel the level of subdivision
		\param maxError maximum error allowed by 'propagated' facet
		\param errorMeasure error measure
		\param useRetroProjectionError whether to use retro-projection error in propagation
		\param progressCb progeress callback
		\return a negative value if something went wrong
	**/
	int init(	CCCoreLib::DgmOctree* theOctree,
				unsigned char gridLevel,
				ScalarType maxError,
				CCCoreLib::DistanceComputationTools::ERROR_MEASURES errorMeasure,
				bool useRetroProjectionError,
				CCCoreLib::GenericProgressCallback* progressCb = nullptr);

	//! Updates a list of point flags, indicating the points alreay processed
	/** \return the number of newly flagged points
	**/
	unsigned updateFlagsTable(	ccGenericPointCloud* theCloud,
								std::vector<unsigned char>& flags,
								unsigned facetIndex);

	//! Sets the propagation progress callback
	void setPropagateCallback(CCCoreLib::GenericProgressCallback* propagateProgressCb) { m_propagateProgressCb = propagateProgressCb; m_propagateProgress = 0; }

	//inherited methods (see FastMarchingAlgorithm)
	virtual int propagate() override;
	virtual bool setSeedCell(const Tuple3i& pos) override;

protected:

	//! A Fast Marching grid cell for planar facets extraction
	class PlanarCell : public CCCoreLib::FastMarching::Cell
	{
	public:
		//! Default constructor
		PlanarCell()
			: Cell()
			, N(0, 0, 0)
			, C(0, 0, 0)
			, cellCode(0)
			, planarError(0)
		{}

		///! Destructor
		~PlanarCell() override {}

		//! The local cell normal
		CCVector3 N;
		//! The local cell center
		CCVector3 C;
		//! the code of the equivalent cell in the octree
		CCCoreLib::DgmOctree::CellCode cellCode;
		//! Cell planarity error
		ScalarType planarError;
	};

	//inherited methods (see FastMarchingAlgorithm)
	float computeTCoefApprox(CCCoreLib::FastMarching::Cell* currentCell, CCCoreLib::FastMarching::Cell* neighbourCell) const override;
	int step() override;
	void initTrialCells() override;
	bool instantiateGrid(unsigned size) override { return instantiateGridTpl<PlanarCell*>(size); }

	//! Adds a given cell's points to the current facet and returns the resulting RMS
	ScalarType addCellToCurrentFacet(unsigned index);

	//! Current facet points
	CCCoreLib::ReferenceCloud* m_currentFacetPoints;

	//! Current facet error
	ScalarType m_currentFacetError;

	//! Max facet error
	ScalarType m_maxError;

	//! Error measrue
	CCCoreLib::DistanceComputationTools::ERROR_MEASURES m_errorMeasure;

	//! Whether to use retro-projection error in propagation
	bool m_useRetroProjectionError;

	//! Propagation progress callback
	CCCoreLib::GenericProgressCallback* m_propagateProgressCb;
	//! Propagation progress
	unsigned m_propagateProgress;
};
